#region New BSD License

// Copyright (c) 2012, John Batte
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
// 
// Redistributions of source code must retain the above copyright notice, this list of conditions
// and the following disclaimer.
// 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions
// and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#endregion

using System;
using System.Collections.Generic;
using System.Linq;

using FizzWare.NBuilder;

using Patterns.Collections;
using Patterns.ExceptionHandling;
using Patterns.Runtime;
using Patterns.Specifications.Common;
using Patterns.Specifications.Subjects;

using TechTalk.SpecFlow;

namespace Patterns.Specifications.Steps
{
	[Binding]
	public class TestAutomation
	{
		public const string Start = "abracadabra";
		public const string Finish = "arbadacarba";
		public const string PropertyWriteValue = "the dog jumped over the fence";

		private static readonly string _resultKey = ScenarioContext.Current.NewKey();
		private static readonly string _addToTestBucketLogicKey = ScenarioContext.Current.NewKey();

		public static object CallResult
		{
			get { return ScenarioContext.Current.SafeGet<object>(_resultKey); }
			private set { ScenarioContext.Current[_resultKey] = value; }
		}

		public static Action<ITestBucket, TestSubject> AddToTestBucketLogic
		{
			get { return ScenarioContext.Current.SafeGet<Action<ITestBucket, TestSubject>>(_addToTestBucketLogicKey); }
			set { ScenarioContext.Current[_addToTestBucketLogicKey] = value; }
		}

		[When(@"I try to write to a property value that throws an exception on the subject")]
		public void WriteAngryProperty()
		{
			Try.Do(() => TestFactory.TestySubject.AngryProperty = PropertyWriteValue);
		}

		[When(@"I try to write to a property value on the subject")]
		public void WriteProperty()
		{
			Try.Do(() => TestFactory.TestySubject.Property = PropertyWriteValue);
		}

		[When(@"I try to call a method that throws an exception on the subject")]
		public void CallAngryMethod()
		{
			CallResult = Try.Get(() => TestFactory.TestySubject.AngryReverseText(Start));
		}

		[When(@"I try to read a value that throws an exception from the subject")]
		public void ReadAngryProperty()
		{
			CallResult = Try.Get(() => TestFactory.TestySubject.AngryProperty);
		}

		[When(@"I try to call a method on the subject")]
		public void CallMethod()
		{
			CallResult = Try.Get(() => TestFactory.TestySubject.ReverseText(Start));
		}

		[When(@"I try to read a property value from the subject")]
		public void ReadProperty()
		{
			CallResult = Try.Get(() => TestFactory.TestySubject.Property);
		}

		[When(@"I add (.+) item(s)? to the collection using the AddRange extension")]
		public void AddRange(int count, string trailingS)
		{
			IList<TestSubject> testSubjects = count <= 0 ? new List<TestSubject>() : Builder<TestSubject>.CreateListOfSize(count).Build();

			try
			{
				TestFactory.SubjectCollection.AddRange(testSubjects);
			}
			catch (ArgumentNullException ex)
			{
				CallResult = ex;
			}
		}

		[When(@"I add a null set to the collection using the AddRange extension")]
		public void AddRangeNull()
		{
			TestFactory.SubjectCollection.AddRange(null);
		}

		[When(@"I run my ""add to test bucket"" logic using Each( with parallel set to true)?")]
		public void AddToTestBucketWithEach(string parallelMode)
		{
			bool parallel = !string.IsNullOrEmpty(parallelMode);
			var bucket = TestFactory.Mocks.Resolve<ITestBucket>();
			TestFactory.SubjectSet.Each(subject => AddToTestBucketLogic(bucket, subject), parallel);
		}

		[When(@"I store the results of both the DateTime\.Now property and the IDateTimeInfo\.GetNow method")]
		public void StoreDateTimeNowVsIDateInfoGetNow()
		{
			TestObservations.SecondaryDateTime = DateTime.Now;
			TestObservations.PrimaryDateTime = TestFactory.DateTimeInfo.GetNow();
		}

		[When(@"I adjust the accuracy of each DateTime value to one second")]
		public void AdjustDateTimeAccuracyToOneSecond()
		{
			TestObservations.PrimaryDateTime = TestObservations.PrimaryDateTime.AccurateToOneSecond();
			TestObservations.SecondaryDateTime = TestObservations.SecondaryDateTime.AccurateToOneSecond();
		}

		[When(@"I compare the DateTime values")]
		public void CompareDateTimeValues()
		{
			TestObservations.DateTimeDelta = TestObservations.PrimaryDateTime.GetDifference(TestObservations.SecondaryDateTime);
		}

		[When(@"I read the options of the CompiledRegex")]
		public void ReadCompiledRegexOptions()
		{
			TestObservations.RegularExpressionOptions = TestFactory.RegularExpression.Options;
		}

		[When(@"I use the CompiledRegex against each string in the set")]
		public void TestRegexAgainstInputStrings()
		{
			TestObservations.RegularExpressionMatches = TestFactory.RegularExpressionInputStrings.Select(item => TestFactory.RegularExpression.Match(item));
		}

		[When(@"I retrieve a dictionary match of the string using the CompiledRegex")]
		public void RegexDictionaryMatchAgainstSingleInput()
		{
			TestObservations.RegularExpressionMatchDictionary = TestFactory.RegularExpression.DictionaryMatch(TestFactory.RegularExpressionSingleInputString);
		}

		[When(@"I read the pattern string from the CompiledRegex")]
		public void ReadRegexPatternString()
		{
			TestObservations.RegularExpressionExtractedPattern = TestFactory.RegularExpression.ToString();
		}
	}
}